package com.vpen.iot.client;

import com.vpen.iot.client.handler.ClientInitializer;
import com.vpen.iot.manager.LinkManager;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.DefaultThreadFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author Vpen
 * @date 2023/7/8 11:35
 */
@Slf4j
@Component
public class IotClient {

    /**
     * 重试计数器
     */
    private static final Map<SocketAddress, Integer> connectCountMap = new ConcurrentHashMap<>();
    private static final int maxCount = 5;


    private static final EventLoopGroup CLIENT_GROUP = new NioEventLoopGroup(4, new DefaultThreadFactory("clint-group", true));

    private static final Bootstrap CLIENT = new Bootstrap();

    @Resource
    private ClientInitializer clientInitializer;

    @PostConstruct
    private void init() {
        this.start();
    }

    @PreDestroy
    private void destroy() {
        this.shutdown();
    }

    /**
     * 连接远程
     *
     * @param inetSocketAddress:
     * @return io.netty.channel.Channel
     */
    public static Channel connect(InetSocketAddress inetSocketAddress) {
        return connect(inetSocketAddress.getHostString(), inetSocketAddress.getPort());
    }

    /**
     * 连接
     *
     * @param ip:
     * @param port:
     */
    public static Channel connect(String ip, int port) {
        InetSocketAddress address = InetSocketAddress.createUnresolved(ip, port);
        ChannelFuture future = CLIENT
                .connect(address)
                .syncUninterruptibly();
        if (future.isSuccess()) {
            connectCountMap.remove(address);
            log.info("[下级平台][主链路]和上级平台{}成功建立连接", address);
            return future.channel();
        } else {
            Integer count = connectCountMap.getOrDefault(address, 0);
            if (count >= maxCount) {
                // null
                return LinkManager.getLinkInfo(address).getChannel();
            }
            EventLoop eventLoop = future.channel().eventLoop();
            eventLoop.schedule(() -> {
                connect(ip, port);
            }, 10L, TimeUnit.SECONDS);
        }
        return future.channel();
    }



    private void start() {
        CLIENT
                .group(CLIENT_GROUP)
                .channel(NioSocketChannel.class)
                .handler(clientInitializer)
                .option(ChannelOption.SO_KEEPALIVE, Boolean.TRUE)
                .option(ChannelOption.TCP_NODELAY, Boolean.TRUE);
        log.debug("客户端配置已经配置完成....");
    }


    private void shutdown() {
        log.error("客户端已关闭");
        CLIENT_GROUP.shutdownGracefully();
    }
}
